package com.xfcy.blog.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xfcy.blog.common.R;
import com.xfcy.blog.common.constant.ScheduleConstants;
import com.xfcy.blog.common.constant.SqlConstant;
import com.xfcy.blog.dto.JobDTO;
import com.xfcy.blog.entity.User;
import com.xfcy.blog.exception.CustomerException;
import com.xfcy.blog.exception.TaskException;
import com.xfcy.blog.mapper.JobMapper;
import com.xfcy.blog.entity.Job;
import com.xfcy.blog.mapper.UserMapper;
import com.xfcy.blog.quartz.CronUtils;
import com.xfcy.blog.quartz.ScheduleUtils;
import com.xfcy.blog.service.JobService;
import com.xfcy.blog.utils.BeanCopyUtil;
import com.xfcy.blog.utils.LocalDateTimeUtils;
import com.xfcy.blog.vo.JobVO;
import jdk.nashorn.internal.scripts.JO;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 定时任务调度表(Job)表服务实现类
 *
 * @author 晓风残月Lx
 * @since 2023-04-09 11:03:06
 */
@Service("jobService")
public class JobServiceImpl extends ServiceImpl<JobMapper, Job> implements JobService {

    @Resource
    private Scheduler scheduler;

    @Resource
    private UserMapper userMapper;

    /**
     * 项目启动时，初始化定时器，主要防止手动修改数据库导致未同步到定时任务处理
     *  （注：不能手动修改数据库ID和任务组名，否则会导致脏数据）
     * @throws SchedulerException
     * @throws TaskException
     */
    @PostConstruct
    private void init() throws SchedulerException, TaskException {
        scheduler.clear();
        List<Job> jobList = baseMapper.selectList(null);
        for (Job job : jobList) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
    }


    /**
     * 定时任务列表
     * @param pageNo
     * @param pageSize
     * @param jobName
     * @param jobGroup
     * @param status
     * @return
     */
    @Override
    public R listJob(Long pageNo, Long pageSize, String jobName, String jobGroup, String status) {
        Page<Job> jobPage = baseMapper.selectPage(new Page<>(pageNo, pageSize),
                new QueryWrapper<Job>().eq(StringUtils.isNotBlank(jobName), SqlConstant.JOB_NAME, jobName)
                        .eq(StringUtils.isNotBlank(jobGroup), SqlConstant.JOB_GROUP, jobGroup)
                        .eq(StringUtils.isNotBlank(status), SqlConstant.STATUS, status));

        return R.success(jobPage);
    }

    /**
     * 删除定时任务
     * @param jobId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R deleteJob(Long jobId) throws SchedulerException {
        Job job = baseMapper.selectById(jobId);
        int deleteById = baseMapper.deleteById(jobId);
        if (deleteById > 0) {
            scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, job.getJobGroup()));
        }
        return R.success();
    }

    /**
     * 批量删除定时任务
     * @param ids
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R deleteBatchJob(List<Long> ids) {
        baseMapper.deleteBatchIds(ids);
        ids.forEach(id -> {
            Job job = baseMapper.selectById(id);
            int deleteById = baseMapper.deleteById(id);
            if (deleteById > 0) {
                try {
                    scheduler.deleteJob(ScheduleUtils.getJobKey(id, job.getJobGroup()));
                } catch (SchedulerException e) {
                    e.printStackTrace();
                }
            }
        });
        return R.success();
    }

    /**
     * 获取定时任务详情
     * @param jobId
     * @return
     */
    @Override
    public R getJobById(Long jobId) {
        Job job = baseMapper.selectById(jobId);
        JobVO jobVO = BeanCopyUtil.copyObject(job, JobVO.class);
        Date nextExecution = CronUtils.getNextExecution(job.getCronExpression());
        jobVO.setNextValidTime(LocalDateTimeUtils.formateLocalDateTime(nextExecution));
        return R.success(job);
    }

    /**
     * 修改定时任务
     * @param jobDTO 调度信息
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateJob(JobDTO jobDTO) throws SchedulerException, TaskException {
        checkCronIsValid(jobDTO.getCronExpression());

        Job job = BeanCopyUtil.copyObject(jobDTO, Job.class);
        User user = userMapper.selectById(StpUtil.getLoginIdAsInt());
        job.setUpdateBy(user.getUsername());
        Job properties = baseMapper.selectById(job.getJobId());
        int update = baseMapper.updateById(job);
        if (update > 0) {
            updateSchedulerJob(job, properties.getJobGroup());
        }

        return R.success();
    }


    /**
     * 添加定时任务
     * @param jobDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R addJob(JobDTO jobDTO) throws SchedulerException, TaskException {
        checkCronIsValid(jobDTO.getCronExpression());

        Job job = BeanCopyUtil.copyObject(jobDTO, Job.class);
        User user = userMapper.selectById(StpUtil.getLoginIdAsInt());
        job.setCreateBy(user.getUsername());
        int insert = baseMapper.insert(job);
        if (insert > 0) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
        return  R.success();
    }

    /**
     * 更改状态
     * @param jobDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R changeStatus(JobDTO jobDTO) throws SchedulerException {
        int update = baseMapper.update(null, new UpdateWrapper<Job>().set(StringUtils.isNotBlank(jobDTO.getStatus()), SqlConstant.STATUS, jobDTO.getStatus()).eq(SqlConstant.JOB_ID, jobDTO.getJobId()));
        if (update > 0) {
            if (ScheduleConstants.Status.NORMAL.getValue().equals(jobDTO.getStatus())) {
                scheduler.resumeJob(ScheduleUtils.getJobKey(jobDTO.getJobId(), jobDTO.getJobGroup()));
            } else if (ScheduleConstants.Status.PAUSE.getValue().equals(jobDTO.getStatus())) {
                scheduler.pauseJob(ScheduleUtils.getJobKey(jobDTO.getJobId(), jobDTO.getJobGroup()));
            }
        }
        return R.success();
    }

    /**
     * 立即执行
     * @param job
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R run(Job job) {
        try {
            Long jobId = job.getJobId();
            String jobGroup = job.getJobGroup();
            // 参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(ScheduleConstants.TASK_PROPERTIES, job);
            scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);
            return R.success();
        } catch (Exception e) {
            throw new CustomerException("定时任务运行失败！失败原因:" + e.getMessage());
        }
    }


    // 自定义方法=========

    /**
     * 验证cron
     * @param cronExpression
     */
    private void checkCronIsValid(String cronExpression) {
        boolean valid = CronUtils.isValid(cronExpression);
        Assert.isTrue(valid,"Cron表达式无效!");
    }

    /**
     * 更新任务
     * @param job
     * @param jobGroup
     */
    private void updateSchedulerJob(Job job, String jobGroup) throws SchedulerException, TaskException {
        Long jobId = job.getJobId();
        // 判断是否存在
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除，然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, job);
    }
}


